package com.cw.utils.java;

import com.cw.utils.java.ip.IPAddressUtils;
import org.apache.commons.lang3.StringUtils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 业务编码+24位流水号(4位(ip最后一段)+15位(时间戳)+5位(重复累加))
 * （备注：李兆河有个三方要求30位。所以后半段24位目前是最高位数，无法在增加）
 * ip获取为空时使用下面的方案
 * 业务编码+22位流水号(2位(节点ID)+15位(时间戳)+5位(重复累加))
* @author chenwei
*/ 
public class SltasSerioNoWorker {
		// 毫秒内自增位
		private final static long sequenceBits = 11L;

		private final static long sequenceMask = -1L ^ (-1L << sequenceBits);

		private static long lastTimestamp = -1L;

		private static long sequence = 0L;
		
		
		/**
		 * 获取节点ID。节点ID变成IP，为了保持原有位数不变，ip用0补成4位；
		 */
		public static String nodeId= "0"+ IPAddressUtils.getLocalIP();
		

		public static synchronized String nextId(String serviceConstant) {
			if(StringUtils.isBlank(serviceConstant)){
				serviceConstant = "";
			}
			long timestamp = timeGen();
			if (timestamp < lastTimestamp) {
				try {
					throw new Exception("Clock moved backwards.  Refusing to generate id for " + (lastTimestamp - timestamp)
							+ " milliseconds");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}

			if (lastTimestamp == timestamp) {
				// 当前毫秒内，则+s1
				sequence = (sequence + 1) & sequenceMask;
				if (sequence == 0) {
					// 当前毫秒内计数满了，则等待下一秒
					timestamp = tilNextMillis(lastTimestamp);
				}
			} else {
				sequence = 0;
			}
			lastTimestamp = timestamp;
			// ID偏移组合生成最终的ID，并返回ID
			Date nowTime = new Date(timestamp);
			DateFormat format=new SimpleDateFormat("yyMMddHHmmssSSS");
			String time = format.format(nowTime);
			String relNum="";
			relNum = "00000"+sequence;
			relNum = relNum.substring(relNum.length()-5);
			String nextId = null;
			//nodeId不足4位，进行补位
			if(nodeId.length()<4){
				String temp="";
				for(int i=0;i<(4-nodeId.length());i++){
					temp+="0";
				}
				nodeId=temp+nodeId;
			}
			nextId = serviceConstant+nodeId+time+relNum;
			return nextId;
		}

		private static long tilNextMillis(final long lastTimestamp) {
			long timestamp = timeGen();
			while (timestamp <= lastTimestamp) {
				timestamp = timeGen();
			}
			return timestamp;
		}

		private static long timeGen() {
			return System.currentTimeMillis();
		}

	public static void main(String[] args) {
		String s =null;
		for (int i = 0; i < 1000; i++) {
			s = SltasSerioNoWorker.nextId("T304");
			//s = SltasSerioNoWorker.nextId(null);
			System.out.println(s);
		}

    }

	/**
	 * 场景1：mobile等前端也生成了，T304的流水号
	 * 那么mobile和transfer，相同的节点，同一秒生成的流水号就会有很大几率重复（因为前面都相同，末位都是从0开始追加的）。
	 * 那么serviceConstant就需要传transfer+mobile的服务业务标识。如T304401
	 * com.sltas.application.common.constant.Domain
	 */
}
